home *** CD-ROM | disk | FTP | other *** search
- Path: j.cc.purdue.edu!mentor.cc.purdue.edu!noose.ecn.purdue.edu!samsung!uunet!papaya.bbn.com!rsalz
- From: rsalz@bbn.com (Rich Salz)
- Newsgroups: comp.sources.unix
- Subject: v23i032: Run a program under a pty session, Part02/06
- Message-ID: <2882@litchi.bbn.com>
- Date: 10 Oct 90 14:16:28 GMT
- Organization: BBN Systems and Technologies, Cambridge MA
- Lines: 1689
- Approved: rsalz@uunet.UU.NET
- X-Checksum-Snefru: 6fe1a084 64d83c14 ac39c37e bf52d9f3
-
- Submitted-by: Dan Bernstein <brnstnd@kramden.acf.nyu.edu>
- Posting-number: Volume 23, Issue 32
- Archive-name: pty/part02
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # The tool that generated this appeared in the comp.sources.unix newsgroup;
- # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
- # Contents: INSTALLREADABLE master.c pty.c
- # Wrapped by rsalz@litchi.bbn.com on Wed Oct 10 10:11:37 1990
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 2 (of 6)."'
- if test -f 'INSTALLREADABLE' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'INSTALLREADABLE'\"
- else
- echo shar: Extracting \"'INSTALLREADABLE'\" \(22621 characters\)
- sed "s/^X//" >'INSTALLREADABLE' <<'END_OF_FILE'
- XThis is a typescript of INSTALL on a Sun 4 running SunOS 4.0.3.
- XI've inserted some further comments at spots where INSTALL might give
- Xdifferent results on different machines.
- X
- X
- X
- XHi, and welcome to the pty install script.
- Xpty is a program for managing pseudo-terminals.
- X
- XI'm not actually going to install anything.
- XI'll just guide you through what has to be done,
- Xfrom compiling through installation.
- X
- XOne advantage of this hands-off philosophy is that
- Xyou can kill and restart this script at any time.
- XYou may prefer to read through INSTALLREADABLE,
- Xwhich has the same information.
- X
- XIn the first part of this script, I'll lead you through configuration.
- XFor the moment, don't actually change anything in your system. Just
- Xalternate between reading this script and fooling around with the
- XMakefile, and I'll remind you later, after compilation, of what has
- Xto be changed elsewhere.
- X
- XI need the following programs: echo, tr [-d], man, sed [-n], grep, test.
- X
- X----- Press return to continue.
- X
- XThe first versions of pty were designed on a BSD 4.2-based system.
- XDuring May 1990 pty 3.0 was written completely from scratch,
- Xon a BSD 4.3-based system. It has been thoroughly tested on
- Xseveral BSD 4.3-based systems, tested with a few Makefile changes
- Xon several BSD 4.2-based systems, and run on at least one mutant.
- X
- XIf you make it through installation and testing and get pty running,
- Xplease send a note to the author, Dan Bernstein, on the Internet
- Xat brnstnd@nyu.edu. Let him know your computer model, OS version, and
- Xwhat changes you had to make. If you have any trouble, please also get
- Xin touch with the author. If you have a different kind of system with
- Xpseudo-terminal support that could use a pty port, the author would
- Xlove to hear about it.
- X
- XOne note: Like all software, pty comes without warranty, to the extent
- Xpermitted by applicable law. Use it at your own risk.
- X
- X----- Press return to continue.
- X
- XI assume that you don't want to make any major changes
- Xto your computer just to take advantage of pty's capabilities.
- XYou might not even have a privileged account, for all I know.
- X
- XThe first major decision you have to make is what privileges to give pty.
- XObviously if you don't have privileges then pty won't either;
- Xthis is Case UN.
- X
- X----- Press return to continue.
- X
- XThere are several good reasons for giving programs privileges.
- XMost importantly, privileged programs can precisely control access
- Xto a shared resource. pty supports an interactive user list in
- X/etc/utmp, a login-logout record in /usr/adm/wtmp, and controlled
- Xaccess to the pseudo-terminal files themselves, including security and
- Xmode changing. None of these are possible if pty isn't setuid.
- X
- XIf your system has a lot more tty security than usual, you probably
- Xwon't be able to run pty without privileges. (However, if your system
- Xhas a lot more tty security than usual, it's almost certainly running
- Xpty anyway.)
- X
- X----- Press return to continue.
- X
- XIf you do want pty to have privileges, you have to decide between
- Xsetting up pty as root (Case ROOT) or as some other user, say ``pty''
- X(Case PTY). Staying away from root has the advantage that problems
- Xcan't possibly destroy all security in one blow. However, root has
- Xseveral advantages, mainly because of BSD limitations: 1. A process
- Xcan't change ownership of a file between its euid and uid. Only root
- Xcan change ownership. It shouldn't be necessary to change ownership
- Xof ptys anyway, but that's what many programs assume. 2. MAXUPRC in
- X<sys/param.h> limits the number of processes under a given *effective*
- Xuid. This absolutely idiotic behavior is a serious problem for programs
- Xsetuid to anything other than root. pty tries to work around this
- Xproblem, but there are no true solutions. 3. Despite the documentation
- Xin kill(2), many machines don't allow non-root processes to send
- XCONT to children with a different uid. Unlike almost all other programs,
- Xpty sensibly handles its child stopping and restarting; but this won't
- Xwork on those machines if pty isn't root.
- X
- XYou should decide now between Case ROOT and Case PTY.
- X
- X----- Press return to continue.
- X
- XWhether you've decided on ROOT, PTY, or UN,
- Xnow is the time to check the security of the entire pty package.
- XIt'd be difficult for anything to slip through the checks by the
- Xauthor, testers, and source group moderator, but you should take
- Xat least a moment to look for your pet security peeves.
- X
- XA couple of security notes: The only open(,O_CREAT) in pty is in
- Xmaster.c, at the top of master(), of a file with a very restricted
- Xform constructed from the filename of a /dev/ttyxx. There are no
- Xcreat()s. The only bind() is in sock.c's pty_readsock(), which is
- Xcalled only from master.c's master() to create a socket with a
- Xsimilarly restricted name. (fnsty is changed in sigler but not in
- Xmaster.) The only exec() is in slave.c, after a forced setreuid()
- Xto a uid that is only set to getuid(). unlink(), mkdir(), and rename()
- Xare only used with restricted pathnames.
- X
- X----- Press return to continue.
- X
- XIn Case PTY, you have to set up a new uid, say ``pty'' (or whatever
- Xusername you want). pty should not allow logins. Its password should
- Xbe an asterisk; its home directory should be /nonexistent; its shell
- Xshould be /bin/true. In other words, the only access to user pty
- Xshould be through these setuid programs.
- X
- XIn any privileged case you should have group tty,
- Xwhich most BSD 4.3 systems have as gid 4.
- X
- XRunning $ ttygroup="`sed -n 's/^tty:[^:]*:\(.*\):.*/\1/p' < /etc/group`" ...
- X
- XI see that you have a tty group, 4 as usual. Good.
- X
- X[Note: Under SunOS 4.0.3, ttygroup comes out to 4. On systems where it
- Xcomes out to nothing at all, you have to add the line tty:4:*:root to
- X/etc/group. On systems where the tty group is something other than 4,
- Xjust remember it.]
- X
- X----- Press return to continue.
- X
- XNext, we're going to drudge through the config.h file.
- X
- XIn Cases ROOT or PTY, you have to set up a system-wide directory
- Xfor storing information about pty sessions. (A session is a program
- Xstuck under a pty that you can disconnect and reconnect.) Users can
- Xget around this directory, storing the information in ~/.pty and
- Xrevoking any privileges, with pty -xS; you can even make this default
- Xby setting flagxsetuid = 0 in globals.c. However, a single system-wide
- Xdirectory is safer.
- X
- XAnyway, that directory, PTYDIR, should be mode 0700, owner root in case
- XROOT or pty in case PTY, group irrelevant. PTYDIR is defined in config.h
- Xas /usr/etc/pty by default; if you choose a different directory, add
- X-DPTYDIR=\"/what/ever/dir/ect/ory\" to CCOPTS in the Makefile.
- X
- XIn Case UN, you may want to set up a directory ~/PTY inside your home
- Xdirectory, and set PTYDIR to that; you may want to set flagxsetuid = 0
- Xin globals.c; or you may want to just type -xS to pty all the time. I
- Xrecommend the first strategy.
- X
- X----- Press return to continue.
- X
- XNext come pseudo-terminal pathnames. I assume that your ptys are
- Xlabelled as /dev/ptyxx and /dev/ttyxx, where xx is any two-letter
- Xextension. pty can support any initial strings instead of these,
- Xbut some of the utilities have /dev/tty hardcoded, and lots of other
- Xprograms running around assume those names. If you're desperate,
- Xdefine DEVMTY and DEVSTY in the Makefile, and figure out what has to
- Xbe changed in util/*. (Sorry.)
- X
- X----- Press return to continue.
- X
- XThe extension is traditionally [p-za-o][0-9a-f]. The first letter of the
- Xsecond string is special: if /dev/ttyq0 doesn't exist, for example, then
- Xnone of /dev/ttyq[0-9a-f] will be used. (That's called a pty bank.)
- X
- XYou can change those strings by defining, e.g., PTY1=\"ABCDEFG\" or
- XPTY2=\"02468abfq\" in the Makefile. You have at least four choices here:
- X1. Give pty its own, new, banks of new pseudo-terminals with weird bank
- Xnames not including a through z, and define PTY1 appropriately. pty will
- Xonly use those new ptys, and older programs will just use the old ptys.
- XThis may require reconfiguring your system for the larger number of
- Xterminals. 2. Give pty some new banks, and take out some old ones to
- Xmake up for it. 3. Give pty a chunk of the old banks. 4. Give pty all
- Xthe old banks.
- X
- XNote that pty searches randomly through PTY1 and PTY2 by default; for
- Xefficiency, PTY1 really should reflect exactly the pty banks you have.
- X
- X----- Press return to continue.
- X
- XSo now you've decided on new ptys, or set aside some or all of your
- Xold ptys, and changed PTY1 and PTY2 appropriately. Because BSD gives a
- Xprocess very little control over its controlling terminal, pty has to
- Xpass a terminal name explicitly when it reconnects. If any of your tty
- X(not just pseudo-tty!) filenames are longer than 30 characters, you have
- Xto set -DTTYNAMELEN=60 (or whatever) in the Makefile.
- X
- X----- Press return to continue.
- X
- X/etc/utmp has traditionally listed all users logged on.
- XThis nebulous concept has evolved through the years; given the way
- Xthat most programs use utmp, utmp is probably better defined as a
- Xlist of all interactive sessions. Anyway, pty supports this file,
- Xand will make an entry in it when given -xu. If you have a different
- Xfile, add -DPTYUTMP_FILE=\"/foo/bar/utmp\" to DEFINES in the Makefile.
- X
- XNote that /etc/utmp is unprotected (mode 666) on Suns. This was Sun's
- Xattempt to let unprivileged programs manage the file. Unforunately, this
- Xerror in judgment opens up a huge security hole, which even on
- Xsingle-user machines is an invitation to make mistakes. I advise you to
- Xmake /etc/utmp owned by root in Case ROOT or pty in Case PTY, mode 644.
- XThere aren't many unprivileged programs that don't understand the
- Xpty interface; people on non-Suns have survived so far, and there's
- Xno reason for you to keep subscribing to an insecure model of resource
- Xsharing.
- X
- X----- Press return to continue.
- X
- XAlthough pty will put an entry into utmp, it doesn't really support
- Xthe remote-host field. There is no logical association between sessions
- Xand connections; why should a login require a pty, and why should a
- Xsession only stick around for one connection? So pty just puts "pty" in
- Xthe host field. You can change this with -DPTYUTMP_HOST=\"foo\"; you may
- Xeven want to make PTY_UTMPHOST call a function that you define.
- X
- X(Similar comments apply to PTYWTMP_HOST. There are also PTYUTMP_SWHOST
- Xand PTYWTMP_SWHOST, defaults PTYUTMP_HOST and "pty-sessuser"
- Xrespectively; see util/sessuser.1 for more information.)
- X
- X----- Press return to continue.
- X
- X/usr/adm/wtmp has traditionally recorded all logins, logouts, reboots,
- Xftp sessions, and various other interesting events. (The entry for a
- Xline in utmp is the last entry for that line in wtmp.) pty supports wtmp
- Xfully, in the same way as utmp. You should make wtmp mode 644, owner
- Xroot or pty as appropriate. Change PTYWTMP_FILE if /usr/adm/wtmp is
- Xsomewhere else.
- X
- X----- Press return to continue.
- X
- XYou can turn off utmp support by uncommenting the NO_UTMP line in
- Xconfig.h. (Just delete the initial /*.) You can also turn off support
- Xfor wtmp, disconnectable sessions, or changing pseudo-terminal file
- Xownership; just uncomment the appropriate line. You can force use of
- Xeach of these features by defining MUST_UTMP, etc., in the same way.
- X
- XNote that the usual login programs do their own wtmp management, and
- Xmany accounting programs incorrectly equate session time with connect
- Xtime, so users will rarely want wtmp. As more programs appear using the
- Xpty interface, and as login programs start using a more logical system
- Xfor accounting, MUST_WTMP may become an appropriate way to monitor
- Xpseudo-terminal usage. For the moment, I do not advise setting any NO
- Xor MUST.
- X
- X----- Press return to continue.
- X
- Xpty subscribes (not very willingly) to the BSD 4.3 model of pty
- Xprotection, with a few twists. All ptys are under group tty, at all
- Xtimes; if you don't have a tty group, all ptys should be under some
- Xother protected group at all times. Anyway, pty will change the group of
- Xeach pseudo-terminal file to PTYGROUP, default 4, under -xc.
- X
- X
- XSince you already have a tty group, gid 4, you're fine.
- X
- X[Note: If your tty group is something other than 4, add -DPTYGROUP=6 (or
- Xwhatever) to the Makefile.]
- X
- X
- X(Under Case UN, don't worry about all this group stuff. Just leave
- XPTYGROUP alone.)
- X
- X----- Press return to continue.
- X
- XUnder -xc, pty changes the pseudo-terminal owner to the current real
- Xuid. The fact that terminals need to be in the file hierarchy reflects a
- Xserious failure in the BSD terminal model; but anyway, many programs
- Xexpect to be able to fool around with /dev/tty. After pty is done with
- Xthe terminal, it sets the owner back to its effective uid. You can
- Xmodify this behavior by changing PTYOWNER from euid to something else.
- X
- XUnder Case UN, PTYOWNER is irrelevant.
- X
- X----- Press return to continue.
- X
- XWhile a tty is unused, BSD systems traditionally leave it unprotected.
- XUnfortunately, this leaves some huge security holes. Here's one of
- Xthe biggest advantages of a privileged pty manager: unused ttys don't
- Xhave to be left open for any random program to use. After it's done
- Xwith a pseudo-terminal, pty changes it to mode UNUSEDPTYMODE, default
- X0600. (This is another case of a poor model for kludging pty support
- Xinto unprivileged programs. If you haven't set up separate pty banks
- Xfor pty, and you really want to allow unprivileged access to unused
- Xptys, try mode 0660 and make those other programs setgid to tty.)
- X
- XA tty that someone's using can be in many different modes. Typically
- XUSEDPTYMODE should be 0600, which means user-only access, messages off,
- Xbiff off. If you really, really, really want to make the mistake of
- Xhaving messages or biff on by default, try 0620, 0700, or 0720. Note
- Xthat the world protection should always be 0: the point of the tty group
- Xis that only setgid-tty programs can access ttys.
- X
- XAs usual, you can't do anything about these in Case UN.
- X
- X----- Press return to continue.
- X
- XNow we're past generic configuration and down to your system's
- Xnitty-gritty.
- X
- XFor purity, pty has SIGRET_TYPE, default int, as the type returned by a
- Xsignal handler. On Suns and ANSI-compliant machines, you should define
- XSIGRET_TYPE=void. Then you can admire the lint -haxc *.c output.
- X
- X----- Press return to continue.
- X
- XOUTBUFSIZE, default 16384, is the size of the buffer pty keeps between
- Xthe incoming data and the pseudo-tty, and between the pseudo-tty and the
- Xoutgoing data. If you want to spare less than 32K per pty just for
- Xbuffer space, feel free to change this.
- X
- X----- Press return to continue.
- X
- XGENERIC is another concession to ANSI taste. GENERIC * should be a
- Xpointer type that any other pointer can be safely converted to and back;
- XGENERIC is char by default, but on newer machines can be set to void.
- XAs it'll be at least five years before ANSI manages to outlaw char *,
- Xand as void * is often invalid, I advise you to leave GENERIC alone.
- X
- X----- Press return to continue.
- X
- Xpty attempts to forward window-size changes transparently. It also
- Xsupports the auxiliary characters structure, which lets you type a
- Xsingle character to get system and tty status.
- X
- XRunning $ man 4 tty | sed 's///g' | grep -s winsize
- XReformatting page. Wait... done
- X
- XI see you don't have window sizes.
- XYou'll have to comment out #define TTY_WINSIZE in config.h.
- X(This may be inaccurate on Suns; try grep winsize /usr/include/sys/tt*.h.)
- X
- X[Note: As a matter of fact, the test is inaccurate under SunOS 4.0.3,
- Xwhich does have window sizes. If you have window sizes, you don't have
- Xto do anything.]
- X
- XRunning $ man 4 tty | sed 's///g' | grep -s auxchars
- XReformatting page. Wait... done
- X
- XI see you don't have auxiliary characters.
- X
- X[Note: This is the usual. If you have auxiliary characters and want pty
- Xto understand them, uncomment #define TTY_AUXCHARS in config.h.]
- X
- X----- Press return to continue.
- X
- Xpty also puts siginterrupt() and usleep() to good use in working
- Xaround BSD limitations. Without siginterrupt(), there is absolutely
- Xno way to get per-process non-blocking I/O, so pty may block when it
- Xdoesn't have to (namely, when it has N bytes to write to the output,
- Xthe output is a pipe with M bytes of space, and N > M > 0). If you
- Xdefine SIGINTERRUPT (as default), pty will take some extra effort to
- Xnever, ever, ever block when it doesn't have to.
- X
- XAt one point, pty pauses to kludge around a common bug in UNIX-domain
- Xsockets. With usleep() it will pause much more briefly. This makes
- Xreconnects much faster.
- X
- XRunning $ man siginterrupt | sed 's///g' | grep -s siginterrupt
- XReformatting page. Wait... done
- X
- XI see you have siginterrupt(). Good.
- X
- XWeird, are you BSD 4.3?
- X
- X[Note: Without siginterrupt(), you have to comment out #define
- XSIGINTERRUPT in config.h.]
- X
- XRunning $ man usleep | sed 's///g' | grep -s usleep
- XReformatting page. Wait... done
- X
- XI see you have usleep(). Good.
- X
- X[Note: Without usleep(), you have to comment out #define USLEEP in
- Xconfig.h.]
- X
- X----- Press return to continue.
- X
- XFinally (almost done with configuration!), we have to make sure that you
- Xhave UNIX-domain sockets, and check whether you have file descriptor
- Xpassing.
- X
- XRunning $ test -r /usr/include/sys/un.h
- X
- XI see you have UNIX-domain sockets. Good.
- X
- X[Note: If <sys/un.h> isn't there, you probably don't have UNIX-domain
- Xsockets, which means you can't have disconnectable sessions. Define
- XNO_UNIXSOCKS in config.h.]
- X
- XRunning $ man recvmsg | sed 's///g' | grep -s rights
- XReformatting page. Wait... done
- X
- XI see you have file descriptor passing. Good. (Your fd passing may be
- Xbuggy, as it's a relatively new, powerful, and rarely exploited
- Xfeature. If you have trouble, try defining NO_FDPASSING.)
- X
- X[Note: If there isn't a recvmsg() man page mentioning access rights, you
- Xprobably don't have file descriptor passing. This isn't a disaster,
- Xthough it means that reconnected sessions will be as slow as they are in
- Xprograms other than pty. Define NO_FDPASSING.]
- X
- X----- Press return to continue.
- X
- XWhew! We've finally made it through configuration. If you want, try
- Xrunning lint -haxc *.c or whatever your favorite code checker is; the
- Xlint here gives some bogus errors about correct char * casts, an error
- Xbecause the SIG_IGN definition is unportable, and a couple of bzero
- Xcomplaints because FD_ZERO isn't defined very well.
- X
- XIf you're on an old system without FD_ZERO, FD_SET, and FD_ISSET in
- X<sys/types.h>, I recommend you upgrade. Try copying file.h.old to file.h
- X(the original version is in file.h.new).
- X
- XWe're nearly at the end. Change CC and CCOPTS in the Makefile for any
- Xlast-minute additions; if you're worried, change -s to -g for debugging.
- XNow compile! % (date; make) >>& Makelog & and come back to this script.
- X
- X----- Press return to continue.
- X
- XWhile the program is compiling, let's start providing some support.
- X(If the compile finishes or has an error, just ignore it for the
- Xmoment.) These are some real changes, so watch out!
- X
- XFirst---in Case PTY only!---make a new uid, pty, not allowing logins.
- X
- X/etc/passwd: pty:*:whatever:4:::/bin/true
- X
- X----- Press return to continue.
- X
- XSecond, make sure you have a tty group set up.
- X
- X/etc/group: tty:*:4:root
- X
- X----- Press return to continue.
- X
- XThird, make PTYDIR.
- X
- XROOT: # mkdir /usr/etc/pty; chmod 700 /usr/etc/pty
- XPTY: # mkdir /usr/etc/pty; chown pty /usr/etc/pty; chmod 700 /usr/etc/pty
- XUN: % mkdir ~/PTY; chmod 700 ~/PTY
- X
- X----- Press return to continue.
- X
- XFourth, make sure you have all the pseudo-terminal files set up and
- Xappropriately configured.
- X
- XChange each of them to group tty.
- X
- XChange each of them to owner pty in case PTY or root in case ROOT.
- X
- XChange each of them to mode UNUSEDPTYMODE (0600).
- X
- X----- Press return to continue.
- X
- XFifth, make sure /etc/utmp and /usr/adm/wtmp exist, owned by root in
- XCase ROOT or pty in Case PTY, each mode 644. (In Case UN, you probably
- Xwant to be working on a Sun with its insecure /etc/utmp, so that pty can
- Xtake advantage of it. Sigh.)
- X
- X----- Press return to continue.
- X
- XSixth, I advise that you make a special directory /usr/local/ptybin for
- Xpty and its associated utilities, with symbolic links in /usr/local.
- XThis choice isn't too important, but it's easier if you know a
- Xplace to put programs.
- X
- X----- Press return to continue.
- X
- XOkay. You've done about all that can be done before compilation
- Xfinishes, so go do something else.
- X
- X----- Press return to continue.
- X
- XThe compile should be done now. If all went well, the make should have
- Xexited normally, Makelog should show no errors, and you should have an
- Xexecutable pty program sitting in this directory. Unfortunately, life
- Xisn't always so generous. Here are a few of the more common errors.
- X
- XThe loader complains about missing getopt(), optarg, and optind: These
- Xare all in the getopt library, available in the early volumes of
- Xcomp.sources.unix. If you use GNU getopt, please make sure you
- Xunderstand the implications of its license.
- X
- XThe loader complains about missing FD_ZERO, FD_SET, and FD_ISSET: Try
- Xcopying file.h.old to file.h and recompiling. If the compiler gives
- Xvarious further errors related to these macros, uncomment the
- Xcommented-out lines in file.h.old, copy to file.h, and try once again.
- X
- Xbcopy() isn't defined: In pty.h, change bcopy(src,dst,num) to
- Xmemcpy(dst,src,num). (Note the order.) Try again.
- X
- XIf you still can't get the code to compile, let the author know.
- X
- X----- Press return to continue.
- X
- XNext, compile the utilities. There isn't much to say about them;
- Xthey're all public domain, some of them clones of or improvements
- Xover standard utilities, some of them clones with special features to
- Xwork with pty, some of them entirely new programs for pty's new
- Xfeatures. Just glance at the top of util/Makefile; then
- X% cd util; (date; make) >>& Makelog & and wait.
- X
- XWarning: The pty session directory is hardcoded in util/sessutil.c.
- XIf you have changed PTYDIR, check through sessutil.c carefully.
- X
- X----- Press return to continue.
- X
- XSo now you should have some executables sitting around collecting dust.
- XIn Cases ROOT and PTY, put {pty,biff,disconnect,mesg,reconnect,sesskill,
- Xsesslist,sessname,sessuser} into a system-wide directory, owner root or
- Xpty, mode 4755. Put wall and write into the same place, group tty, mode
- X2755. Put {condom,excloff,exclon,last,lock,script,script.tidy,sess,tiocsti,
- Xtty,u,who,xdisconnect,xreconnect,xsesskill,xsesslist,xsessname,xsessuser}
- Xinto the same place, mode 755. Make absolutely sure none of the shell
- Xscripts are setuid.
- X
- XIn Case UN, just put all the programs somewhere in your PATH.
- X
- XThat's it! Try running TESTS now.
- X
- X(Two other things you have to do at some point: move your old script,
- Xbiff, mesg, and so on to script.old, biff.old, mesg.old, etc., with
- Xsymbolic links to the new versions in their place; and move the old
- X/usr/man/{script,biff,mesg,...}.1 somewhere else, copy all the *.1
- Xhere to /usr/man/man1, and run /etc/catman.)
- END_OF_FILE
- if test 22621 -ne `wc -c <'INSTALLREADABLE'`; then
- echo shar: \"'INSTALLREADABLE'\" unpacked with wrong size!
- fi
- # end of 'INSTALLREADABLE'
- fi
- if test -f 'master.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'master.c'\"
- else
- echo shar: Extracting \"'master.c'\" \(18086 characters\)
- sed "s/^X//" >'master.c' <<'END_OF_FILE'
- X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */
- X
- X#include <sys/types.h>
- X#include <sys/time.h>
- X#include <sys/resource.h>
- X#include <sys/wait.h>
- X#include <stdio.h>
- X#include "err.h"
- X#include "config.h"
- X#include "pty.h"
- X#include "master.h"
- X#include "sig.h"
- X#include "tty.h"
- X#include "file.h"
- X#include "sock.h"
- X#include "logs.h"
- X#include "misc.h"
- X
- Xstatic char fnre[20];
- X
- Xstatic char fnsess[20];
- Xstatic int fdsess;
- X
- Xstatic char *glfnsty;
- X
- Xstatic char soutbuf[OUTBUFSIZE];
- Xstatic char sptybuf[OUTBUFSIZE];
- X
- Xstatic struct ttymodes tmowinpty;
- Xstatic struct ttymodes tmowintty;
- X
- Xstatic char *outbuf = soutbuf;
- Xstatic int outbufsiz = OUTBUFSIZE;
- Xstatic int outbuflen = 0;
- Xstatic char *ptybuf = sptybuf;
- Xstatic int ptybufsiz = OUTBUFSIZE;
- Xstatic int ptybuflen = 0;
- X
- Xstatic int flagconnected = 1; /* 0: disconnected. 2: idling for stop. */
- X /* 3: idling for stop but child is dead. */
- Xstatic int flagchild = 1; /* 0: dead. 2: stopped. */
- Xstatic int childsig; /* signal that stopped/killed child */
- Xstatic int flagsigler = 1; /* 0: dead. */
- Xstatic int siglerpid; /* only defined if flagconnected */
- Xstatic int slavepid;
- X
- Xstatic int flagqwinch = 0;
- X
- Xstatic void quickdeath(i)
- Xint i;
- X{
- X /* All exits from master() go through here. */
- X if (flagsession) (void) unlink(fnsess);
- X if (flagxchown)
- X (void) fchown(fdsty,PTYOWNER,PTYGROUP);
- X (void) fchmod(fdsty,UNUSEDPTYMODE);
- X date = now();
- X if (flagxutmp)
- X if (utmp(glfnsty + PTYUTMP_OFFSET,"","",date) == -1)
- X ; /* too bad. */
- X if (flagxwtmp)
- X if (wtmp(glfnsty + PTYWTMP_OFFSET,"","",date) == -1)
- X ; /* too bad. */
- X fatal(i);
- X}
- X
- Xstatic void death(i)
- Xint i;
- X{
- X (void) kill(siglerpid,SIGTERM);
- X /* XXX: should wait while flagsigler */
- X quickdeath(i);
- X}
- X
- X/*ARGSUSED*/
- Xstatic void sig_force(i)
- Xsig_num i;
- X{
- X /* Forced death, presumably from the sesskill program. */
- X sig_ignore(SIGCHLD);
- X /* XXX: Should we test for !flagchild here? sesskill does. */
- X flagchild = 0;
- X quickdeath(SIGCHLD);
- X}
- X
- X/*ARGSUSED*/
- Xstatic void sig_usr2(i)
- Xsig_num i;
- X{
- X if (flagsession)
- X {
- X int newuid = uid;
- X char newsuid[10];
- X char foo[100];
- X
- X /* XXX: We should have some error recovery here! */
- X
- X (void) lseek(fdsess,(long) 0,0);
- X (void) read(fdsess,(char *) &newuid,sizeof(int));
- X (void) sprintf(newsuid,"%d",newuid);
- X
- X (void) chdir("..");
- X if (chdir(newsuid) == -1)
- X {
- X (void) mkdir(newsuid,0700);
- X (void) chdir(newsuid);
- X }
- X
- X (void) sprintf(foo,"../%d/%s",uid,fnsess);
- X (void) rename(foo,fnsess);
- X
- X (void) sprintf(foo,"../%d/%s",uid,fnre);
- X (void) rename(foo,fnre); /* in case we're already disconnected */
- X
- X uid = newuid;
- X (void) setreuid(uid,euid);
- X setusername();
- X
- X if (flagxutmp)
- X if (utmp(glfnsty + PTYUTMP_OFFSET,username,PTYUTMP_SWHOST,date) == -1)
- X ; /* too bad. */
- X if (flagxwtmp)
- X if (wtmp(glfnsty + PTYWTMP_OFFSET,username,PTYWTMP_SWHOST,date) == -1)
- X ; /* too bad. */
- X if (flagsigler)
- X (void) kill(siglerpid,SIGUSR2);
- X }
- X}
- X
- X/*ARGSUSED*/
- Xstatic void sig_pipe(i)
- Xsig_num i;
- X{
- X flagsigler = 0; /* XXX: is this appropriate? race? */
- X /* Will end up giving child HUP. */
- X}
- X
- X/*ARGSUSED*/
- Xstatic void sig_chld(i)
- Xsig_num i;
- X{
- X union wait w;
- X
- X if (wait3(&w,WNOHANG | WUNTRACED,(struct rusage *) 0) <= 0)
- X return; /* why'd we get the CHLD? it must have stopped & restarted? */
- X
- X if (w.w_stopval == WSTOPPED)
- X {
- X childsig = w.w_stopsig;
- X flagchild = 2;
- X }
- X else
- X {
- X childsig = w.w_termsig; /* can't do much with this */
- X flagchild = 0;
- X }
- X}
- X
- X/*ARGSUSED*/
- Xstatic void sig_term(i)
- Xsig_num i;
- X{
- X flagsigler = 0;
- X}
- X
- X/* If we have made it to being master, we should never get TTIN or TTOU, */
- X/* except possibly while restarting after a stop (e.g., if the user puts */
- X/* us back into the background). But we let the signaller handle putting */
- X/* the tty modes back before restarting us, so we should never, ever, */
- X/* ever get a TTIN or TTOU. If the user is messing around and we do get */
- X/* a TTIN or TTOU, we'll just pretend the child died and hope we get */
- X/* around to telling the signaller about it. */
- X
- X/*ARGSUSED*/
- Xstatic void sig_ttin(i)
- Xsig_num i;
- X{
- X if (flagchild)
- X {
- X childsig = SIGTTIN;
- X flagchild = 2;
- X }
- X}
- X
- X/*ARGSUSED*/
- Xstatic void sig_ttou(i)
- Xsig_num i;
- X{
- X if (flagchild)
- X {
- X childsig = SIGTTOU;
- X flagchild = 2;
- X }
- X}
- X
- X/*ARGSUSED*/
- Xstatic void sig_tstp(i)
- Xsig_num i;
- X{
- X if (flagchild)
- X {
- X childsig = SIGCONT;
- X flagchild = 2;
- X }
- X}
- X
- X/* Most job-control shells (including csh) behave absolutely miserably. */
- X/* (Well, that goes without saying.) In particular, rather than sending */
- X/* a CONT to every one of their children in the process group, they feel */
- X/* a need to kill the entire process group. Grrrr. Because of this, we */
- X/* are forced to use the nonintuitive USR1 to communicate CONT, and ignore */
- X/* CONT entirely. Anyway, read cont as usr1 where necessary. */
- X
- X/* We can only get USR1 from the signaller (or from us after reconnect). */
- X/* By convention, the signaller handles setting the tty modes back to */
- X/* chartty, even though we handled restoring the modes before stop. */
- X
- X/*ARGSUSED*/
- Xstatic void sig_cont(i)
- Xsig_num i;
- X{
- X if (flagchild)
- X {
- X flagchild = 1;
- X (void) kill(slavepid,SIGCONT);
- X (void) kill(pid,SIGWINCH);
- X }
- X if (flagconnected == 3)
- X flagconnected = 1; /* XXX: should be internal to master() */
- X (void) setpgrp(0,pgrp);
- X}
- X
- X/* If it weren't for WINCH, which must be in the master if NO_FDPASSING, */
- X/* and for the stupid conventions surrounding a process's control tty, */
- X/* then all mention of fdtty could disappear from master. This would */
- X/* slightly complicate the signaller's T{STP,TIN,TOU} handling but make */
- X/* reconnect a lot simpler. Sigh. */
- X
- X/*ARGSUSED*/
- Xstatic void sig_winch(i)
- Xsig_num i;
- X{
- X int pg;
- X
- X flagqwinch = 0;
- X#ifdef TTY_WINDOWS
- X/* An unfortunate but slight race: Another handler could change the pgrp */
- X/* if the child suddenly stops and we're queued for delivery. So we have */
- X/* to change it back. */
- X pg = getpgrp(0);
- X (void) setpgrp(0,pgrp);
- X if (!flagsigler)
- X flagqwinch = 1;
- X else
- X if (tty_getmodes(fdsty,&tmopty) == 0)
- X if (tty_getmodes(fdtty,&tmowintty) == 0)
- X {
- X tty_copymodes(&tmowinpty,&tmopty);
- X tty_copywin(&tmowinpty,&tmowintty);
- X (void) tty_modifymodes(fdsty,&tmowinpty,&tmopty);
- X }
- X (void) setpgrp(0,pg);
- X#endif
- X}
- X
- Xstatic int disconnect(fnsty)
- Xchar *fnsty;
- X{
- X if (fdtty != -1)
- X {
- X (void) tty_dissoc(fdtty); /* must succeed */
- X (void) close(fdtty);
- X fdtty = -1;
- X }
- X if (fdpass != -1)
- X {
- X /* We used to write the dot to fdpass here. It's in sigler now, to */
- X /* prevent a race condition. */
- X (void) close(fdpass);
- X fdpass = -1;
- X }
- X if (fdin != -1)
- X {
- X (void) close(fdin);
- X fdin = -1;
- X }
- X if (fdout != -1)
- X {
- X (void) close(fdout);
- X fdout = -1;
- X }
- X if (fdre != -1)
- X {
- X (void) close(fdre);
- X fdre = -1;
- X }
- X
- X fdre = pty_readsock(fnsty,fnre);
- X if (fdre == -1)
- X return -1; /* damn. */
- X return 0;
- X}
- X
- Xstatic int reconnect()
- X{
- X int t;
- X char buf[1];
- X char fntty[TTYNAMELEN]; /* sigh */
- X int flags = 0;
- X
- X t = pty_acceptsock(fdre);
- X (void) close(fdre);
- X fdre = t;
- X if (fdre == -1)
- X return -1;
- X
- X#define VCF (void) close(fdre)
- X#define BONK(xxx,yyy) if ((xxx) == -1) { VCF; return -1; } else (yyy);
- X
- X/* What about fd 2 for warnings & errors? No, master doesn't use them. */
- X
- X/* Must have: in, out, siglerpid, pgrp, flagjobctrl. 1, 2, 16, 32, 256. */
- X/* Except if NO_FDPASSING: just flagjobctrl in that case. */
- X/* If fdtty, must have also tmochartty, tmotty, fntty. 8: 64, 128, 1024. */
- X/* Finally, fdpass is independent of all the rest. */
- X
- X/* CHANGE: With fdpass, fdin and fdout are irrelevant. */
- X
- X if (pty_sendint(fdre,'p',&pid) == -1)
- X {
- X VCF;
- X return -1;
- X }
- X
- X while (pty_getch(fdre,buf) == 0)
- X switch(buf[0])
- X {
- X#ifdef NO_FDPASSING
- X case 's': BONK(pty_putgetstr(fdre,'s',fntty),flags |= 8) break;
- X#else
- X case '0': BONK(pty_putgetfd(fdre,'0',&fdin),flags |= 1) break;
- X case '1': BONK(pty_putgetfd(fdre,'1',&fdout),flags |= 2) break;
- X case 'f': BONK(pty_putgetfd(fdre,'f',&fdpass),flags |= 4) break;
- X case 't': BONK(pty_putgetfd(fdre,'t',&fdtty),flags |= 8) break;
- X case 's': BONK(pty_putgetstr(fdre,'s',fntty),flags |= 1024) break;
- X#endif
- X case 'p': BONK(pty_putgetint(fdre,'p',&siglerpid),flags |= 16) break;
- X case 'g': BONK(pty_putgetint(fdre,'g',&pgrp),flags |= 32) break;
- X case 'c': BONK(pty_putgettty(fdre,'c',&tmochartty),flags |= 64) break;
- X case 'n': BONK(pty_putgettty(fdre,'n',&tmotty),flags |= 128) break;
- X case 'j': BONK(pty_putgetint(fdre,'j',&flagjobctrl),flags |= 256) break;
- X#ifdef NO_FDPASSING
- X case ' ': if ((flags & 256) != 256) { VCF; return -1; }
- X#else
- X case ' ': if (flags & 4) flags |= 3;
- X if ((flags & 307) != 307) { VCF; return -1; }
- X if (flags & 8) if ((flags & 1024) != 1024) { VCF; return -1; }
- X#endif
- X if (flags & 8) if ((flags & 192) != 192) { VCF; return -1; }
- X
- X#ifdef NO_FDPASSING
- X if ((fdtty = open(fntty,O_RDWR)) == -1)
- X return -1;
- X if ((fdin = dup(fdre)) == -1)
- X {
- X (void) close(fdtty);
- X fdtty = -1;
- X return -1;
- X }
- X if ((fdout = dup(fdre)) == -1)
- X {
- X (void) close(fdtty);
- X fdtty = -1;
- X (void) close(fdout);
- X fdout = -1;
- X return -1;
- X }
- X#endif
- X VCF; /* yahoo! */
- X (void) close(open(fntty,O_RDWR));
- X /* XXX: do we really have to reattach? */
- X /* I wish there were no concept of controlling tty. */
- X /* Instead, an ioctl on /dev/tty (i.e., fd 3) would */
- X /* return a session identifier. */
- X
- X if (fdpass != -1)
- X {
- X if (pty_sendint(fdpass,'G',&siglerpid) == -1)
- X return -1;
- X /* XXX: death(1) might be more intuitive. Then */
- X /* again, it may also be much more destructive. */
- X if (pty_sendfd(fdpass,'m',&fdmty) == -1)
- X return -1;
- X if (pty_sendfd(fdpass,'s',&fdsty) == -1)
- X return -1;
- X }
- X
- X /* So that we can disconnect again, we have to reset the */
- X /* siglerpid in fdsess. That done, we've totally severed */
- X /* our previous link to a connection. */
- X (void) lseek(fdsess,(long) sizeof(int),0);
- X (void) write(fdsess,(char *) &siglerpid,sizeof(int));
- X
- X flagsigler = 1;
- X (void) setpgrp(0,pgrp);
- X (void) kill(pid,SIGUSR1); /* grrrr */
- X return 0;
- X default: (void) pty_putch(fdre," "); break;
- X }
- X VCF;
- X return -1;
- X}
- X
- Xstruct timeval instant = { 0, 0 };
- X
- Xvoid master(fnsty,child)
- Xchar *fnsty;
- Xint child;
- X{
- X fd_set rfds;
- X fd_set wfds;
- X int fdnum;
- X int r;
- X
- X /* XXX: is it a race for child to set pty modes? */
- X
- X /* Note that we don't close fdsty. */
- X
- X siglerpid = getppid();
- X slavepid = child;
- X pid = getpid();
- X glfnsty = fnsty;
- X
- X if (flagsession)
- X {
- X /* Security note: This is the only file we actually create, */
- X /* not counting the reconnect socket. */
- X (void) sprintf(fnsess,"sess.%s",fnsty + sizeof(DEVSTY) - 3);
- X fdsess = open(fnsess,O_RDWR | O_CREAT | O_TRUNC,0600);
- X (void) write(fdsess,(char *) &uid,sizeof(int));
- X (void) write(fdsess,(char *) &siglerpid,sizeof(int));
- X (void) write(fdsess,(char *) &pid,sizeof(int));
- X (void) write(fdsess,(char *) &slavepid,sizeof(int));
- X /* We'll never actually bother closing fdsess. Who cares? */
- X }
- X
- X sig_ignore(SIGURG);
- X sig_ignore(SIGIO);
- X sig_ignore(SIGHUP);
- X sig_ignore(SIGQUIT);
- X sig_ignore(SIGINT);
- X sig_sethandler(SIGXCPU,sig_force); sig_handle(SIGXCPU);
- X sig_ignore(SIGXFSZ);
- X sig_ignore(SIGPROF);
- X sig_ignore(SIGVTALRM);
- X
- X sig_default(SIGEMT); /* XXX: really dump? */
- X sig_default(SIGIOT);
- X sig_default(SIGTRAP);
- X sig_default(SIGSYS);
- X sig_default(SIGFPE);
- X sig_default(SIGILL);
- X sig_default(SIGSEGV);
- X
- X sig_default(SIGSTOP);
- X
- X sig_sethandler(SIGTTIN,sig_ttin); sig_handle(SIGTTIN);
- X sig_sethandler(SIGTTOU,sig_ttou); sig_handle(SIGTTOU);
- X sig_sethandler(SIGTSTP,sig_tstp); sig_handle(SIGTSTP);
- X sig_sethandler(SIGUSR1,sig_cont); sig_handle(SIGUSR1);
- X sig_ignore(SIGCONT); /* grrrr. see explanation above sig_cont. */
- X sig_sethandler(SIGPIPE,sig_pipe); sig_handle(SIGPIPE);
- X
- X sig_sethandler(SIGCHLD,sig_chld); sig_handle(SIGCHLD);
- X
- X sig_sethandler(SIGTERM,sig_term); sig_handle(SIGTERM);
- X sig_sethandler(SIGWINCH,sig_winch); sig_handle(SIGWINCH);
- X
- X sig_sethandler(SIGUSR2,sig_usr2); sig_handle(SIGUSR2);
- X
- X if (fdpass != -1)
- X {
- X if (pty_sendint(fdpass,'G',&siglerpid) == -1)
- X death(1);
- X if (pty_sendfd(fdpass,'m',&fdmty) == -1)
- X death(1);
- X if (pty_sendfd(fdpass,'s',&fdsty) == -1)
- X death(1);
- X }
- X
- X#define SET_FDNUM fdnum = fdin; if (fdout > fdnum) fdnum = fdout; \
- Xif (fdmty > fdnum) fdnum = fdmty; fdnum++;
- X
- X SET_FDNUM
- X
- X if (fdpass == -1)
- X (void) fcntl(fdmty,F_SETFL,FNDELAY);
- X /* If it doesn't work, too bad. */
- X
- X#ifdef SIGINTERRUPT
- X sig_interrupt();
- X#endif
- X
- X for (;;)
- X {
- X /* Stage 1: Mangle internal states. This could be made into a */
- X /* critical section, but there's no point. */
- X
- X if ((flagconnected == 2) && (flagchild != 2))
- X flagconnected = 1 + 2 * (flagchild == 0);
- X if ((flagconnected != 0) && (flagsigler == 0))
- X {
- X flagconnected = 0;
- X if (flagsession)
- X {
- X (void) kill(siglerpid,SIGTERM);
- X#ifdef NO_SESSION
- X ; /* impossible */
- X#else
- X if (disconnect(fnsty) == -1)
- X quickdeath(1); /* XXX: sigh */
- X if (fdnum <= fdre)
- X fdnum = fdre + 1;
- X#endif
- X }
- X }
- X
- X /* Stage 2: Prepare fds, and select(). */
- X
- X FD_ZERO(&rfds);
- X FD_ZERO(&wfds);
- X
- X if ((fdpass == -1) && (outbuflen < outbufsiz))
- X FD_SET(fdmty,&rfds);
- X if ((fdpass == -1) && ptybuflen)
- X FD_SET(fdmty,&wfds);
- X if ((fdpass == -1)
- X &&(ptybuflen < ptybufsiz) && (flagsigler == 1)
- X &&(flagconnected == 1) && (flagchild == 1))
- X FD_SET(fdin,&rfds);
- X if ((fdpass == -1)
- X &&(outbuflen) && (flagsigler == 1) && (flagconnected == 1))
- X FD_SET(fdout,&wfds);
- X
- X if (flagsession && (flagconnected == 0))
- X FD_SET(fdre,&rfds);
- X
- X /* The times to flush buffers: when the child has stopped and we're */
- X /* connected; when the child has died and we're connected; when the */
- X /* signaller has died and we don't support sessions. */
- X if (((flagconnected == 1) && (flagchild != 1))
- X ||((flagconnected == 0) && (flagsession == 0)))
- X r = select(fdnum,&rfds,&wfds,(fd_set *) 0,&instant);
- X else
- X r = select(fdnum,&rfds,&wfds,(fd_set *) 0,(struct timeval *) 0);
- X
- X
- X /* Stage 3: Interpret the results and handle special cases. */
- X
- X if (r <= 0)
- X if (r == -1)
- X switch(errno)
- X {
- X case EBADF: death(1);
- X break;
- X case EINTR: break; /* fine. */
- X case EINVAL: break; /* impossible. */
- X default: break; /* say what? */
- X }
- X else /* r is 0 */
- X {
- X if (flagconnected == 1) /* flagchild is 0 or 2 */
- X if (flagchild == 0)
- X break; /* That's it! Child died, and we're outta here! */
- X else
- X { /* done with flush, time to stop sigler & idle */
- X if (flagjobctrl)
- X {
- X /* As usual, if we don't have a tty, tmotty == tmochartty
- X and it won't matter that fdtty is undefined. */
- X (void) setpgrp(0,pgrp);
- X if (tty_modifymodes(fdtty,&tmotty,&tmochartty) == -1)
- X ; /* XXX: what to do? */
- X (void) setpgrp(0,pid);
- X switch(childsig)
- X {
- X case SIGSTOP: (void) kill(siglerpid,SIGSTOP); break;
- X case SIGTTOU: (void) kill(siglerpid,SIGTTOU); break;
- X case SIGTTIN: (void) kill(siglerpid,SIGTTIN); break;
- X case SIGTSTP: (void) kill(siglerpid,SIGTSTP); break;
- X case SIGCONT: break; /* special case---see sig_tstp */
- X default: (void) kill(siglerpid,SIGSTOP); break;
- X }
- X flagconnected = 2;
- X }
- X }
- X else if (flagconnected == 0) /* non-session, sigler dead */
- X break; /* Giving pty pgrp a HUP, ho hum */
- X /* Most pgrp-based killing would be more logically done */
- X /* one process at a time, i.e., we should give our child */
- X /* a signal specially. But nobody else does, so we won't. */
- X }
- X else
- X {
- X#ifndef NO_SESSION
- X if (flagconnected == 0)
- X if (FD_ISSET(fdre,&rfds))
- X if (reconnect() == -1)
- X {
- X if (disconnect(fnsty) == -1)
- X quickdeath(1); /* sigh */
- X if (fdnum <= fdre)
- X fdnum = fdre + 1;
- X }
- X else
- X {
- X flagconnected = 1; /* yay! */
- X SET_FDNUM
- X continue; /* XXX */
- X }
- X#endif
- X
- X
- X /* Stage 4: Do normal I/O. */
- X
- X#ifdef SIGINTERRUPT
- X sig_startring(); /* blocking? never heard of it */
- X#endif
- X
- X if (FD_ISSET(fdin,&rfds))
- X {
- X /* ptybuflen must be smaller than ptybufsiz. */
- X r = read(fdin,ptybuf + ptybuflen,ptybufsiz - ptybuflen);
- X if (r == -1)
- X switch(errno)
- X {
- X case EINTR: case EWOULDBLOCK: break; /* fine */
- X default: death(1);
- X }
- X else if (r == 0) /* EOF */
- X {
- X ; /* XXX: there's no way to pass an EOF */
- X }
- X else
- X ptybuflen += r;
- X }
- X if (FD_ISSET(fdmty,&rfds))
- X {
- X /* outbuflen must be smaller than outbufsiz. */
- X r = read(fdmty,outbuf + outbuflen,outbufsiz - outbuflen);
- X if (r == -1)
- X switch(errno)
- X {
- X case EINTR: case EWOULDBLOCK: break; /* fine */
- X default: death(1);
- X }
- X else if (r == 0) /* EOF */
- X {
- X ; /* This can't happen. The slave can't pass an EOF. */
- X /* XXX: Should we close fdout anyway? */
- X }
- X else
- X outbuflen += r;
- X }
- X if (FD_ISSET(fdout,&wfds))
- X {
- X r = write(fdout,outbuf,outbuflen);
- X if (r == -1)
- X switch(errno)
- X {
- X case EINTR: case EWOULDBLOCK: break; /* fine */
- X default: death(1);
- X }
- X else if (r == 0) /* ? */
- X ; /* impossible */
- X else if (r == outbuflen)
- X outbuflen = 0;
- X else
- X {
- X outbuflen -= r;
- X copy(outbuf,outbuf + r,outbuflen);
- X }
- X }
- X if (FD_ISSET(fdmty,&wfds))
- X {
- X r = write(fdmty,ptybuf,ptybuflen);
- X if (r == -1)
- X switch(errno)
- X {
- X case EINTR: case EWOULDBLOCK: break; /* fine */
- X default: death(1);
- X }
- X else if (r == 0) /* ? */
- X ; /* impossible */
- X else if (r == ptybuflen)
- X ptybuflen = 0;
- X else
- X {
- X ptybuflen -= r;
- X copy(ptybuf,ptybuf + r,ptybuflen);
- X }
- X }
- X
- X#ifdef SIGINTERRUPT
- X sig_stopring();
- X#endif
- X }
- X }
- X
- X death(0);
- X}
- END_OF_FILE
- if test 18086 -ne `wc -c <'master.c'`; then
- echo shar: \"'master.c'\" unpacked with wrong size!
- fi
- # end of 'master.c'
- fi
- if test -f 'pty.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'pty.c'\"
- else
- echo shar: Extracting \"'pty.c'\" \(10563 characters\)
- sed "s/^X//" >'pty.c' <<'END_OF_FILE'
- X/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */
- X
- X/*
- Xpty.c: run a program under a pty session
- X*/
- X
- X#include <stdio.h>
- Xextern unsigned short getuid(); /* grrrr */
- Xextern unsigned short geteuid(); /* grrrr */
- X#include "config.h"
- X#include "getopt.h"
- X#include "err.h"
- X#include "pty.h"
- X#include "tty.h"
- X#include "texts.h"
- X#include "sig.h"
- X#include "sigler.h"
- X#include "master.h"
- X#include "slave.h"
- X#include "file.h"
- X#include "logs.h"
- X#include "misc.h"
- X
- Xint flagpcbreak = 0; /* -pc, character-at-a-time */
- Xint flagpnew = 1; /* -pd, new line discipline---traditionally off to start */
- Xint flagpecho = 1; /* -pe, echo characters */
- Xint flagpcrmod = 1; /* -pn, munge carriage returns */
- Xint flagpraw = 0; /* -pr, raw mode */
- Xint flagpcrt = 1; /* -ps, screen */
- X
- Xgetfreepty(fnmty,fnsty,pty1,pty2)
- Xregister char fnmty[sizeof(DEVMTY)];
- Xregister char fnsty[sizeof(DEVSTY)];
- Xregister char pty1[sizeof(PTY1)];
- Xregister char pty2[sizeof(PTY2)];
- X{
- X register char *c1;
- X register char *c2;
- X register char *c1start; /* for ``random'' pty searching */
- X register char *c2start;
- X int e;
- X
- X if (flagxrandom)
- X {
- X c1start = pty1 + (pid % (sizeof(PTY1) - 1));
- X c2start = pty2 + ((pid + date) % (sizeof(PTY2) - 1));
- X }
- X else
- X {
- X c1start = pty1;
- X c2start = pty2;
- X }
- X
- X c1 = c1start;
- X do
- X {
- X fnmty[sizeof(DEVMTY) - 3] = *c1;
- X fnmty[sizeof(DEVMTY) - 2] = pty2[0];
- X if (!access(fnmty,F_OK))
- X {
- X c2 = c2start;
- X fnsty[sizeof(DEVSTY) - 3] = *c1;
- X fnmty[sizeof(DEVMTY) - 2] = fnsty[sizeof(DEVSTY) - 2] = *c2;
- X do
- X {
- X#ifdef DESPERATE_ALARMS
- X sig_startring();
- X#endif
- X
- X/* Some other process could come along and mess up our test by opening */
- X/* the master side before we do. But in that case they'll get the pty */
- X/* anyway, and we'll move on to another possibility without comment. */
- X if (flagxchkopen)
- X {
- X#ifdef DONT_NDELAY
- X fdsty = open(fnsty,O_RDWR);
- X#else
- X fdsty = open(fnsty,O_RDWR | O_NDELAY);
- X#endif
- X e = errno;
- X fdmty = open(fnmty,O_RDWR);
- X }
- X else
- X {
- X fdmty = open(fnmty,O_RDWR);
- X fdsty = open(fnsty,O_RDWR);
- X e = errno;
- X }
- X
- X#ifdef DESPERATE_ALARMS
- X sig_stopring();
- X#endif
- X
- X if (fdmty != -1)
- X {
- X if (flagxskipopen && (fdsty != -1))
- X warnerr2("pty: warning: slave %s still in use\n",fnsty);
- X else
- X {
- X if ((fdsty == -1) && (e != EINTR) && (e != EWOULDBLOCK))
- X fatalerr2p(6,"pty: fatal: slave %s unopenable",fnsty,e);
- X if (flagxchkopen)
- X if (fdsty == -1)
- X {
- X fdsty = open(fnsty,O_RDWR);
- X e = errno;
- X }
- X else
- X warnerr2("pty: warning: slave %s still in use\n",fnsty);
- X if (fdsty == -1)
- X fatalerr2p(6,"pty: fatal: slave %s unopenable",fnsty,e);
- X else
- X {
- X if (flagxchkopen)
- X if (fcntl(fdsty,F_SETFL,0) == -1)
- X fatalerrp(6,"pty: fatal: can't fcntl pty",e);
- X return 0;
- X }
- X }
- X }
- X
- X if (fdmty != -1) (void) close(fdmty);
- X if (fdsty != -1) (void) close(fdsty);
- X if (!(*(++c2)))
- X c2 = pty2;
- X fnmty[sizeof(DEVMTY) - 2] = fnsty[sizeof(DEVSTY) - 2] = *c2;
- X }
- X while (c2 != c2start);
- X }
- X if (!(*(++c1)))
- X c1 = pty1;
- X }
- X while (c1 != c1start);
- X return -1;
- X}
- X
- Xchar fnmty[sizeof(DEVMTY)] = DEVMTY;
- Xchar fnsty[sizeof(DEVSTY)] = DEVSTY;
- Xchar pty1[sizeof(PTY1)] = PTY1;
- Xchar pty2[sizeof(PTY2)] = PTY2;
- X
- Xmain(argc,argv)
- Xint argc;
- Xchar *argv[];
- X{
- X int opt;
- X int f;
- X
- X uid = getuid();
- X euid = geteuid();
- X pid = getpid();
- X pgrp = getpgrp(0);
- X date = now();
- X setusername();
- X
- X while ((opt = getopt(argc,argv,"qQvdDe3Ef:FjJsStTp:x:0ACHUVW")) != EOF)
- X switch(opt)
- X {
- X case 'A': fatalinfo(1,ptyauthor);
- X case 'C': fatalinfo(1,ptycopyright);
- X case 'H': fatalinfo(1,ptyhelp);
- X case 'U': fatalinfo(1,ptyusage);
- X case 'V': fatalinfo(1,ptyversion);
- X case 'W': fatalinfo(1,ptywarranty);
- X case '?': fatalinfo(1,ptyusage);
- X case 'q': flagquiet = 1; break;
- X case 'Q': flagquiet = 0; flagverbose = 0; break;
- X case 'v': flagverbose = 1; break;
- X case 'd': flagdetached = 1; flagjobctrl = 0; flagttymodes = 0; break;
- X case 'D': flagdetached = 0; flagjobctrl = 1; flagttymodes = 1; break;
- X case 'e': flagsameerr = 2; break;
- X case '3': flagsameerr = 1; break;
- X case 'E': flagsameerr = 0; break;
- X case 'f': flagfdpass = 1;
- X if (sscanf(optarg,"%d",&fdpass) < 1) fatalinfo(1,ptyusage);
- X break;
- X case 'F': flagfdpass = 0; break;
- X case 'j': flagjobctrl = 1; break;
- X case 'J': flagjobctrl = 0; break;
- X case 's': flagsession = 1; flagxutmp = 1; break;
- X case 'S': flagsession = 0; flagxutmp = 0; break;
- X case 't': flagttymodes = 1; break;
- X case 'T': flagttymodes = 0; break;
- X case '0': flagsameerr = 2; flagsession = 0; flagttymodes = 0;
- X flagxutmp = 0; /* XXX: also flagxwtmp = 0? */
- X flagpcbreak = 3; flagpraw = 3; flagpecho = 2; flagpnew = 2;
- X break;
- X case 'p': while (opt = *(optarg++))
- X switch(opt)
- X {
- X case 'c': flagpcbreak = 3; break;
- X case 'C': flagpcbreak = 2; break;
- X case 'd': flagpnew = 3; break;
- X case 'D': flagpnew = 2; break;
- X case 'e': flagpecho = 3; break;
- X case 'E': flagpecho = 2; break;
- X case 'n': flagpcrmod = 3; break;
- X case 'N': flagpcrmod = 2; break;
- X case 'r': flagpraw = 3; break;
- X case 'R': flagpraw = 2; break;
- X case 's': flagpcrt = 3; break;
- X case 'S': flagpcrt = 2; break;
- X case '0': flagpcbreak = 3; flagpraw = 3;
- X flagpecho = 2; flagpnew = 2;
- X break;
- X default: fatalinfo(1,ptyusage); break;
- X }
- X break;
- X case 'x': while (opt = *(optarg++))
- X switch(opt)
- X {
- X case 'c': flagxchown = 1; break;
- X case 'C': flagxchown = 0; break;
- X case 'u': flagxutmp = 1; break;
- X case 'U': flagxutmp = 0; break;
- X case 'w': flagxwtmp = 1; break;
- X case 'W': flagxwtmp = 0; break;
- X case 'x': flagxexcl = 1; break;
- X case 'X': flagxexcl = 0; break;
- X case 'e': flagxerrwo = 1; break;
- X case 'E': flagxerrwo = 0; break;
- X case 'n': flagxchkopen = 1; break;
- X case 'N': flagxchkopen = 0; break;
- X case 'o': flagxskipopen = 1; break;
- X case 'O': flagxskipopen = 0; break;
- X case 'r': flagxrandom = 1; break;
- X case 'R': flagxrandom = 0; break;
- X case 's': flagxsetuid = 1; break;
- X case 'S': flagxsetuid = 0; break;
- X default: fatalinfo(1,ptyusage); break;
- X }
- X break;
- X }
- X argv += optind, argc -= optind;
- X
- X if (!*argv)
- X fatalinfo(1,ptyusage);
- X
- X /* Option forcing. */
- X#ifdef NO_UTMP
- X if (flagxutmp) if (flagverbose) warnerr2("%s","pty: utmp forced off\n");
- X flagxutmp = 0;
- X#endif
- X#ifdef NO_WTMP
- X if (flagxwtmp) if (flagverbose) warnerr2("%s","pty: wtmp forced off\n");
- X flagxwtmp = 0;
- X#endif
- X#ifdef NO_CHOWN
- X if (flagxchown) if (flagverbose) warnerr2("%s","pty: chown forced off\n");
- X flagxchown = 0;
- X#endif
- X#ifdef NO_SESSION
- X if (flagsession) if (flagverbose) warnerr2("%s","pty: session forced off\n");
- X flagsession = 0;
- X#endif
- X#ifdef MUST_UTMP
- X if (flagxutmp) if (flagverbose) warnerr2("%s","pty: utmp forced on\n");
- X flagxutmp = 1;
- X#endif
- X#ifdef MUST_WTMP
- X if (flagxwtmp) if (flagverbose) warnerr2("%s","pty: wtmp forced on\n");
- X flagxwtmp = 1;
- X#endif
- X#ifdef MUST_CHOWN
- X if (flagxchown) if (flagverbose) warnerr2("%s","pty: chown forced on\n");
- X flagxchown = 1;
- X#endif
- X#ifdef MUST_SESSION
- X if (flagsession) if (flagverbose) warnerr2("%s","pty: session forced on\n");
- X flagsession = 1;
- X#endif
- X#ifdef NO_FDPASSING
- X if (flagfdpass) if (flagverbose) warnerr2("%s","pty: fd passing forced off\n");
- X flagfdpass = 0;
- X#endif
- X
- X /* Option munging. */
- X if (flagsession) flagsameerr = 0;
- X if (flagdetached) flagttymodes = 0;
- X if (flagxskipopen) flagxchkopen = 1;
- X
- X if (!flagxsetuid)
- X {
- X (void) setreuid(uid,uid);
- X euid = uid;
- X }
- X
- X
- X sig_init();
- X sig_sethandler(SIGALRM,nothing); sig_handle(SIGALRM);
- X sig_default(SIGTTIN);
- X sig_default(SIGTTOU);
- X
- X if (fdpass == -1) /* wow, was this a source of bugs. */
- X {
- X fdin = 0;
- X fdout = 1;
- X }
- X
- X if (flagdetached)
- X {
- X tty_initmodes(&tmopty,flagpcbreak,flagpnew,flagpecho,
- X flagpcrmod,flagpraw,flagpcrt);
- X }
- X else
- X {
- X if ((fdtty = tty_getctrl()) == -1)
- X fatalerr(2,"pty: fatal: cannot find control terminal; try -d?\n");
- X if (tty_getmodes(fdtty,&tmotty) == -1)
- X fatalerr(3,"pty: fatal: cannot get current tty modes\n");
- X /* XXX: is there a way to recover more gracefully? */
- X tty_copymodes(&tmopty,&tmotty);
- X tty_mungemodes(&tmopty,flagpcbreak,flagpnew,flagpecho,
- X flagpcrmod,flagpraw,flagpcrt);
- X tty_copymodes(&tmochartty,&tmotty);
- X if (flagttymodes)
- X tty_charmode(&tmochartty);
- X }
- X
- X /* XXX: Here would be a good spot to include pty limits, say through */
- X /* the file PTYDIR/LIMITS. Lines of the form user group num, saying */
- X /* that user in that group is limited to num ptys, with * for all. */
- X /* All pty use would have to be logged somewhere. Anyway, with a */
- X /* streams-based pty, there wouldn't be much point to limits. */
- X
- X if (getfreepty(fnmty,fnsty,pty1,pty2) == -1)
- X fatalerr(5,"pty: fatal: no ptys available\n");
- X
- X if (flagverbose)
- X warnerr2("pty: successfully opened pty %s\n",fnsty);
- X
- X if (tty_modifymodes(fdtty,&tmochartty,&tmotty) == -1)
- X {
- X (void) tty_setmodes(fdtty,&tmotty); /* XXX --- gasp */
- X fatalerr(4,"pty: fatal: cannot set modes of original tty\n");
- X }
- X
- X/* In general, BSD systems check MAXUPRC against the effective uid, */
- X/* rather than the real uid; and they check it during a fork(). */
- X/* The combination of these annoying behaviors means that we have */
- X/* to switch uids while forking, hence possibly losing any security */
- X/* measures we may have set up before the fork(). Grrrr. */
- X
- X (void) setreuid(euid,uid);
- X if ((f = fork()) == -1)
- X {
- X (void) tty_modifymodes(fdtty,&tmotty,&tmochartty);
- X fatalerr(7,"pty: fatal: cannot fork once\n");
- X /* After this, the signaller will handle tty modes. */
- X }
- X else if (f == 0)
- X if ((f = fork()) == -1)
- X {
- X (void) kill(pid,SIGTERM); /*XXX*/
- X fatalerr(7,"pty: fatal: cannot fork twice\n");
- X }
- X else if (f == 0)
- X {
- X (void) setreuid(uid,euid);
- X slave(fnsty,argv);
- X }
- X else
- X {
- X (void) setreuid(uid,euid);
- X if (flagsession)
- X if (sessdir() == -1)
- X fatal(1);
- X master(fnsty,f);
- X }
- X else
- X {
- X (void) setreuid(uid,euid);
- X if (flagsession)
- X if (sessdir() == -1)
- X {
- X fatalerr(8,"pty: fatal: cannot change to session directory\n");
- X (void) tty_modifymodes(fdtty,&tmotty,&tmochartty);
- X }
- X sigler(fnsty,f);
- X }
- X
- X fatal(9); /* just in case */
- X /*NOTREACHED*/
- X}
- END_OF_FILE
- if test 10563 -ne `wc -c <'pty.c'`; then
- echo shar: \"'pty.c'\" unpacked with wrong size!
- fi
- # end of 'pty.c'
- fi
- echo shar: End of archive 2 \(of 6\).
- cp /dev/null ark2isdone
- MISSING=""
- for I in 1 2 3 4 5 6 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 6 archives.
- rm -f ark[1-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
- exit 0 # Just in case...
- --
- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
- Use a domain-based address or give alternate paths, or you may lose out.
-